#include "TransformComp.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <SECore/Ecs/Actor.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/euler_angles.hpp>

namespace SEngine
{
    void TransformComp::Attach()
    {
        m_actor.Register<glm::mat4&>("SignalUpdateTransform");
    }

    void TransformComp::OnUpdate(int delta)
    {
        m_actor.Invoke<glm::mat4&>("SignalUpdateTransform", this->GetModel());
    }

    glm::mat4 TransformComp::GetModel()
    {  
        glm::mat4 model = GetLocalModel();  
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return model * comp->get().GetModel();
            }
        }
        return model;
    }

    glm::mat4 TransformComp::GetLocalModel()
    {
        return glm::translate(glm::mat4{1.0f}, translate) *
                    glm::mat4_cast(rotate) *
                    glm::scale(glm::mat4{1.0f}, scale);
    }

    glm::mat4 TransformComp::GetTranslateMatrix()
    {
        glm::mat4 model = glm::translate(glm::mat4{1.0f}, translate);
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return comp->get().GetTranslateMatrix() * model;
            }
        }
        return model;
    }

    glm::mat4 TransformComp::GetScaleMatrix()
    {
        glm::mat4 model = glm::scale(glm::mat4{1.0f}, scale);
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return comp->get().GetScaleMatrix() * model;
            }
        }
        return model;
    }

    glm::mat4 TransformComp::GetRotateMatrix()
    {
        glm::mat4 model = glm::mat4_cast(rotate);
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return comp->get().GetRotateMatrix() * model;
            }
        }
        return model;
    }

    glm::mat4 TransformComp::GetTranslateRotateMatrix()
    {
        glm::mat4 model = glm::translate(glm::mat4{1.0f}, translate) * 
                          glm::mat4_cast(rotate);
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return comp->get().GetTranslateRotateMatrix() * model;
            }
        }
        return model;
    }

    glm::mat4 TransformComp::ToLocalModel(glm::mat4 m)
    {
        if (m_actor.GetParent()) {
            if(auto comp = (*m_actor.GetParent())->GetComponent<TransformComp>(); comp) {
                return m * glm::inverse(comp->get().ToLocalModel(comp->get().GetModel()));
            }
        }
        return m;
    }

    void TransformComp::SetModel(glm::mat4 matrix)
    {
        SetLocalModel(ToLocalModel(matrix));
    }

    void TransformComp::SetLocalModel(glm::mat4 matrix)
    {
        glm::vec3 skew;
        glm::vec4 perspactive;
        glm::decompose(matrix, scale, rotate, translate, skew, perspactive);
    }

} // namespace SEngine
